package com.zhike.service.messages.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.zhike.common.constant.EsConsts;
import com.zhike.common.dto.EsMessageDTO;
import com.zhike.common.dto.GroupInfo4Cache;
import com.zhike.common.dto.SearchPageRespDTO;
import com.zhike.common.repository.HistoryMessage;
import com.zhike.common.vo.UserInfo4Cache;
import com.zhike.common.dto.MessageRespDTO;
import com.zhike.common.dto.MessageSearchReqDTO;
import com.zhike.common.dto.RestResp;
import com.zhike.component.MyElasticsearchClient;
import com.zhike.service.messages.EsSearchService;
import com.zhike.service.system.ISystemService;
import com.zhike.utils.cache.GroupsInfoCacheProvider;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.MatchQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch._types.query_dsl.TermQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.TermsQuery;
import co.elastic.clients.elasticsearch.core.IndexRequest;
import co.elastic.clients.elasticsearch.core.IndexResponse;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.TotalHits;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import com.zhike.service.im.concurrent.CommonConcurrentExecutor;

/**
 * Elasticsearch 搜索 服务实现类
 */
@Service
// @RequiredArgsConstructor
@Slf4j
public class EsSearchServiceImpl implements EsSearchService {

    @Autowired
    private MyElasticsearchClient elasticClient;

    @Autowired
    private ISystemService systemService;

    @Autowired
    private GroupsInfoCacheProvider groupsInfoCacheProvider;

    @SneakyThrows
    @Override
    public RestResp<SearchPageRespDTO<MessageRespDTO>> searchMessage(MessageSearchReqDTO condition) {
        ElasticsearchClient client = elasticClient.getElasticsearchClient();

        SearchResponse<EsMessageDTO> response = client.search(s -> {

            SearchRequest.Builder searchBuilder = s.index(EsConsts.MessageIdx.INDEX_NAME);
            // 构建检索条件
            buildSearchCondition(condition, searchBuilder);
            // 分页
            searchBuilder.from((condition.getPageNum() - 1) * condition.getPageSize())
                    .size(condition.getPageSize());
            // 设置高亮显示
            searchBuilder.highlight(h -> h.fields(EsConsts.MessageIdx.FIELD_MSG_CONTENT,
                    t -> t.preTags("<em style='color:blue'>").postTags("</em>"))
                    .fields(EsConsts.MessageIdx.FIELD_MSG_CONTENT,
                            t -> t.preTags("<em style='color:blue'>").postTags("</em>")));

            return searchBuilder;
        }, EsMessageDTO.class);

        TotalHits total = response.hits().total();
        List<MessageRespDTO> list = new ArrayList<>();
        List<Hit<EsMessageDTO>> hits = response.hits().hits();

        for (Hit<EsMessageDTO> hit : hits) {
            EsMessageDTO message = hit.source();
            UserInfo4Cache sender = systemService.getUserInfo(message.getSenderUid());
            UserInfo4Cache receiver = systemService.getUserInfo(message.getReceiver());
            String gname = "";
            if (StringUtils.isNotBlank(message.getGroupId())) {
                GroupInfo4Cache gc = groupsInfoCacheProvider.get(message.getGroupId());
                if (null != gc) {
                    gname = gc.getGname();
                }
            }

            list.add(MessageRespDTO.builder()
                    .id(hit.id())
                    .sessionId(message.getSessionId())
                    .content(message.getContent())
                    .senderUid(message.getSenderUid())
                    .senderNickname(sender == null ? "" : sender.getNickName())
                    .receiver(message.getReceiver())
                    .receiverNickname(receiver == null ? "" : receiver.getNickName())
                    .fp(message.getFp())
                    .groupId(message.getGroupId())
                    .groupName(gname)
                    .chatType(message.getChatType())
                    .msgType(message.getMsgType())
                    .updateTime(message.getTimestamp())
                    .build());
        }
        return RestResp.ok(
                SearchPageRespDTO.of(condition.getPageNum(), condition.getPageSize(), total.value(), list));

    }

    private void buildSearchCondition(MessageSearchReqDTO condition, SearchRequest.Builder searchBuilder) {
        BoolQuery boolQuery = BoolQuery.of(b -> {
            if (StringUtils.isNotBlank(condition.getGroupId())) {
                b.must((q -> q.multiMatch(t -> t
                        .fields(EsConsts.MessageIdx.FIELD_GROUPID)
                        .query(condition.getGroupId()))));
            }
            if (!StringUtils.isBlank(condition.getKeyword())) {
                // 关键词匹配
                b.must((q -> q.multiMatch(t -> t
                        .fields(EsConsts.MessageIdx.FIELD_MSG_CONTENT)
                        .query(condition.getKeyword()))));
                b.must((q -> q.multiMatch(t -> t
                        .fields(EsConsts.MessageIdx.FIELD_MSG_TYPE)
                        .query("0"))));

                // 加入用户ID条件
                if (StringUtils.isNotBlank(condition.getReceiver())) {
                    b.must((q -> q.multiMatch(t -> t
                            .fields(EsConsts.MessageIdx.FIELD_MSG_SENDERUID)
                            .query(condition.getSenderUid()))));
                    b.must((q -> q.multiMatch(t -> t
                            .fields(EsConsts.MessageIdx.FIELD_MSG_RECEIVER)
                            .query(condition.getReceiver()))));
                } else {
                    condition.setReceiver(condition.getSenderUid());
                    b.should(TermQuery.of(m -> m
                            .field(EsConsts.MessageIdx.FIELD_MSG_SENDERUID)
                            .value(condition.getSenderUid()))._toQuery());
                    b.should(TermQuery.of(m -> m
                            .field(EsConsts.MessageIdx.FIELD_MSG_RECEIVER)
                            .value(condition.getReceiver()))._toQuery());
                }

            } else {
                // 消息类型
                List<FieldValue> fieldValues = new ArrayList<>();

                for (String mt : condition.getMsgType()) {
                    fieldValues.add(FieldValue.of(mt));
                }

                Query query = new TermsQuery.Builder()
                        .field(EsConsts.MessageIdx.FIELD_MSG_TYPE)
                        .terms(t -> t.value(fieldValues))
                        .build()._toQuery();
                b.must(query);

                // 加入用户ID条件
                Query senderQuery = MatchQuery.of(m -> m
                        .field(EsConsts.MessageIdx.FIELD_MSG_SENDERUID)
                        .query(condition.getSenderUid()))._toQuery();
                if (StringUtils.isNotBlank(condition.getReceiver())) {
                    Query receQuery = MatchQuery.of(m -> m
                            .field(EsConsts.MessageIdx.FIELD_MSG_RECEIVER)
                            .query(condition.getReceiver()))._toQuery();
                    b.must(senderQuery, receQuery);
                } else {
                    condition.setReceiver(condition.getSenderUid());
                    Query receQuery = MatchQuery.of(m -> m
                            .field(EsConsts.MessageIdx.FIELD_MSG_RECEIVER)
                            .query(condition.getReceiver()))._toQuery();
                    b.should(senderQuery);
                    b.should(receQuery);
                }

            }

            return b;
        });

        searchBuilder.query(q -> q.bool(boolQuery));
    }

    @Override
    public void insert(HistoryMessage hm) {
        CommonConcurrentExecutor.getInstance().execute(new Runnable() {
            @Override
            public void run() {
                ElasticsearchClient client = elasticClient.getElasticsearchClient();
                // 定义要插入的数据
                Map<String, Object> document = new HashMap<>();
                document.put("id", hm.getId());
                document.put("sessionId", hm.getSessionId());
                document.put("content", hm.getContent());
                document.put("senderUid", hm.getSenderUid());
                document.put("receiver", hm.getReceiver());
                document.put("fp", hm.getFp());
                document.put("chatType", hm.getChatType());
                document.put("msgType", hm.getMsgType());
                document.put("timestamp", hm.getTimestamp());
                document.put("groupId", hm.getGroupId());

                try {
                    // 创建 IndexRequest
                    IndexRequest<Map<String, Object>> indexRequest = IndexRequest.of(i -> i
                            .index("message")
                            .document(document));
                    // 执行插入操作
                    IndexResponse response = client.index(indexRequest);
                    log.debug("Document inserted with ID: {}", response.id());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });

    }

    @Override
    public void insertAll(List<HistoryMessage> hms) {
        for (HistoryMessage hm : hms) {
            insert(hm);
        }
    }

}
